using System;
using System.Collections.Generic;
using System.Diagnostics;
using NHibernate;
using NHibernate.Cfg;
using NHibernate.Tool.hbm2ddl;

namespace WSKnowledge.WSHelpers.NHibernate
{
	/* Consult this url regarding singletons
	 * http://www.yoda.arachsys.com/csharp/singleton.html */

	/// <summary>
	/// This is a singleton sealed class acts as NHiberante Helper for generating
	/// and storing ISessionFactory(ies) and their corresponding Configuration(s).
	/// </summary>
	public sealed class NHibernateService
	{
		/// <summary>
		/// For applications that depends on default configuration file 'hibernate.cfg.xml'.
		/// </summary>
		private Configuration _cfg;
		private ISessionFactory _factory;

		/// <summary>
		/// For applications that depends on special configuration file (e.g. centralsecurity.cfg.xml).
		/// </summary>
		private Dictionary<string, Configuration> _cfgs;
		private Dictionary<string, ISessionFactory> _factories;

		private NHibernateService()
		{
		}

		public static NHibernateService Builder
		{
			get { return SingletonCreator.instance; }
		}

		class SingletonCreator
		{
			internal static readonly NHibernateService instance;
			static SingletonCreator() { instance = new NHibernateService(); }
		}

		/// <summary>
		/// If not already created, This function creates and returns a <see cref="ISessionFactory"/> instance.
		/// </summary>
		/// <returns>ISessionFactory</returns>
		public ISessionFactory GetSessionFactory()
		{
			if (_factory == null)
				_factory = GetConfiguration().BuildSessionFactory();
			return _factory;
		}

		/// <summary>
		/// If not already created, This function creates and returns a <see cref="Configuration"/> instance.
		/// </summary>
		/// <returns></returns>
		public Configuration GetConfiguration()
		{
			if (_cfg == null)
				_cfg = new Configuration().Configure();
			return _cfg;
		}

		/// <summary>
		/// Same as <c>GetSessionFactory</c>, it generates <c>ISessionFactory</c>.
		/// But, for supplied configuration file (not default configuration file 'hibernate.cfg.xml').
		/// </summary>
		/// <returns>ISessionFactory</returns>
		public ISessionFactory GetSessionFactoryFor(string cfgPath)
		{
			if (cfgPath.IsNullOrEmpty())
				return GetSessionFactory();

			if (_factories == null)
				_factories = new Dictionary<string, ISessionFactory>();

			if (_factories.ContainsKey(cfgPath))
				return _factories[cfgPath];

			var sessionFactory = GetConfigurationFor(cfgPath).BuildSessionFactory();
			_factories.Add(cfgPath, sessionFactory);

			return sessionFactory;
		}

		/// <summary>
		/// Same as <c>GetConfiguration</c>, it create <c>Configuration</c>.
		/// But, for supplied configuration file (not default configuration file 'hibernate.cfg.xml').
		/// </summary>
		/// <returns></returns>
		public Configuration GetConfigurationFor(string cfgPath)
		{
			Trace.WriteLine("Creating a new Hibernate Configuration object...\n");

			if (cfgPath.IsNullOrEmpty())
			{
				Trace.WriteLine("configuration path is empty or null.\n");
				return GetConfiguration();
			}

			if (System.IO.File.Exists(cfgPath))
			{
				Trace.WriteLine("--> using configuration path '" + cfgPath + "'.\n");
				if (_cfgs == null) _cfgs = new Dictionary<string, Configuration>();
				if (_cfgs.ContainsKey(cfgPath))
				{
					Trace.WriteLine("--> configuration path is already registered.\n");
					return _cfgs[cfgPath];
				}
				
				Trace.WriteLine("--> configuration path is new and will be registered.\n");
				var cfg = new Configuration().Configure(cfgPath);
				_cfgs.Add(cfgPath, cfg);
				return cfg;
			}
			
			Trace.WriteLine("--> configuration path is not valid (file does not exist).\n");
			throw new System.IO.FileNotFoundException("Could not find the specified NHibernate configuration file", cfgPath);
		}

		/// <summary>
		/// Creates a database schema, generates the ddl script and run it against the database.
		/// If supplied configuration file is null or empty, default configuration file 'hibernate.cfg.xml' will be considered.
		/// </summary>
		public void CreateDatabaseFor(string configPath)
		{
			var cfg = String.IsNullOrEmpty(configPath) ? GetConfiguration() : GetConfigurationFor(configPath);
			var exporter = new SchemaExport(cfg);
			exporter.Create(true, true);
		}
	}
}
